package com.hd123.rumba.gwt.widget2.client.form;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.dom.client.Document;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.hd123.rumba.gwt.widget2.client.fielddef.BooleanFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.EnumFieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.FieldDef;
import com.hd123.rumba.gwt.widget2.client.fielddef.HasFieldDef;
import com.hd123.rumba.gwt.widget2.client.form.RValidator.ValidResult;

/**
 * 单选按钮组
 * 
 * @author LiQi
 * 
 */
@SuppressWarnings("deprecation")
public class RRadioGroup extends RWidgetGroup implements HasText, SourcesChangeEvents,
    HasChangeHandlers, HasRValidator, HasFieldDef {

  /** 样式 */
  private static final String STYLENAME_ERROR = "rb-RadioGroup-error";

  /** RadioButton的name */
  private String groupName;

  /** 单选按钮事件处理 */
  private RadioButtonHandler radioButtonHandler;

  /** 单选按钮数据 */
  private List items;

  /** 当前选中的RadioItem */
  private RadioItem selected;

  /** 是否只读 */
  private boolean readOnly = false;

  /** 验证器 */
  private RValidator validator;

  /** 字段定义 */
  private FieldDef fieldDef;

  public RRadioGroup() {
    super();

    groupName = "RRadioGroup" + Random.nextInt();
    items = new ArrayList();

    radioButtonHandler = new RadioButtonHandler();
  }

  /**
   * 添加一个单选按钮
   * 
   * @param text
   *          显示文本
   * @param value
   *          对应的值
   */
  public void addRadio(String text, Object value) {
    RadioItem item = new RadioItem(text, value);
    items.add(item);

    RadioButton button = new RadioButton(groupName, text);
    button.addClickHandler(radioButtonHandler);
    add(button);
    item.setButton(button);
  }

  /**
   * 添加一个单选按钮
   * 
   * @param text
   *          显示文本，同时也作为对应的值
   */
  public void addRadio(String text) {
    addRadio(text, text);
  }

  /**
   * 根据文本查找对应的单选按钮并删除之
   * 
   * @param text
   */
  public void removeRadioByText(String text) {
    RadioItem item = getItemByText(text);
    removeRadio(item);
  }

  /**
   * 根据值查找对应的单选按钮并删除之
   * 
   * @param value
   */
  public void removeRadioByValue(Object value) {
    RadioItem item = getItemByValue(value);
    removeRadio(item);
  }

  /**
   * 删除按钮及其对应的数据
   * 
   * @param item
   */
  private void removeRadio(RadioItem item) {
    if (item == null)
      return;

    items.remove(item);
    remove(item.getButton());
    if (item.equals(selected))
      selected = null;
  }

  /**
   * 删除所有单选按钮
   * 
   */
  public void removeAllRadio() {
    items.clear();
    clear();
    selected = null;
  }

  /**
   * 当前选中的按钮的文本，如果没有按钮被选中则返回null
   * 
   * @return
   */
  public String getText() {
    return selected == null ? null : selected.getText();
  }

  /**
   * 根据文本查找按钮并选中之，如果没找到按钮则会使当前选中按钮变为空
   * 
   * @param text
   */
  public void setText(String text) {
    RadioItem item = getItemByText(text);
    select(item);
  }

  /**
   * 当前选中的按钮的值对象，如果没有按钮被选中则返回null
   * 
   * @return
   */
  public Object getValue() {
    return selected == null ? null : selected.getValue();
  }

  /**
   * 根据值对象查找按钮并选中之，如果没找到按钮则会使当前选中按钮变为空
   * 
   * @param value
   */
  public void setValue(Object value) {
    RadioItem item = getItemByValue(value);
    select(item);
  }

  /**
   * 清除所有按钮的选中状态，使得getValue()返回null
   * 
   */
  public void clearValue() {
    select(null);
  }

  /**
   * 当前选中的按钮，返回null表示没有任何按钮被选中
   * 
   * @return
   */
  public RadioButton getSelected() {
    return selected == null ? null : selected.getButton();
  }

  /**
   * 选中指定的按钮
   * 
   * @param item
   */
  private void select(RadioItem item) {
    selected = item;
    if (selected != null) {
      // 选中按钮
      if (selected.getButton() != null)
        selected.getButton().setValue(Boolean.TRUE);
    } else {
      // 清除所有按钮的选中状态
      for (Iterator i = items.iterator(); i.hasNext();) {
        RadioItem currentItem = (RadioItem) i.next();
        if (currentItem.getButton() != null)
          currentItem.getButton().setValue(Boolean.FALSE);
      }
    }
  }

  /**
   * 根据RadioButton查找对应的RadioItem
   * 
   * @param button
   * @return 没找到则返回null
   */
  private RadioItem getItemByButton(RadioButton button) {
    if (button == null)
      return null;
    for (Iterator i = items.iterator(); i.hasNext();) {
      RadioItem item = (RadioItem) i.next();
      if (button.equals(item.getButton()))
        return item;
    }
    return null;
  }

  /**
   * 根据文本查找对应的RadioItem
   * 
   * @param text
   * @return 没找到则返回null
   */
  private RadioItem getItemByText(String text) {
    if (text == null)
      return null;
    for (Iterator i = items.iterator(); i.hasNext();) {
      RadioItem item = (RadioItem) i.next();
      if (text.equals(item.getText()))
        return item;
    }
    return null;
  }

  /**
   * 根据值查找对应的RadioItem
   * 
   * @param value
   * @return 没找到则返回null
   */
  private RadioItem getItemByValue(Object value) {
    if (value == null)
      return null;
    for (Iterator i = items.iterator(); i.hasNext();) {
      RadioItem item = (RadioItem) i.next();
      if (value.equals(item.getValue()))
        return item;
    }
    return null;
  }

  /**
   * 是否只读，默认为false
   * 
   * @return
   */
  public boolean isReadOnly() {
    return readOnly;
  }

  /**
   * 设置是否只读，默认为false
   * 
   * @param readOnly
   */
  public void setReadOnly(boolean readOnly) {
    if (this.readOnly == readOnly)
      return;

    this.readOnly = readOnly;
    for (Iterator i = iterator(); i.hasNext();) {
      RadioButton button = (RadioButton) i.next();
      button.setEnabled(!readOnly);
    }
    refreshStyle();
  }

  public boolean isEnabled() {
    return super.isEnabled();
  }

  public void setEnabled(boolean enabled) {
    if (isEnabled() == enabled)
      return;

    super.setEnabled(enabled);
    for (Iterator i = iterator(); i.hasNext();) {
      RadioButton button = (RadioButton) i.next();
      button.setEnabled(enabled);
    }
    refreshStyle();
  }

  /**
   * @deprecated 请用{@link #addChangeHandler}代替
   */
  @Deprecated
  public void addChangeListener(ChangeListener listener) {
    ListenerWrapper.WrappedChangeListener.add(this, listener);
  }

  /**
   * @deprecated 请用{@link #addChangeHandler}返回的
   *             {@link HandlerRegistration#removeHandler}代替
   */
  @Deprecated
  public void removeChangeListener(ChangeListener listener) {
    ListenerWrapper.WrappedChangeListener.remove(this, listener);
  }

  public HandlerRegistration addChangeHandler(ChangeHandler handler) {
    return addDomHandler(handler, ChangeEvent.getType());
  }

  /**
   * 根据当前状态设置样式
   * 
   */
  private void refreshStyle() {
    if (!isReadOnly() && isEnabled() && !isValid()) {
      getGrid().addStyleName(STYLENAME_ERROR);
    } else {
      getGrid().removeStyleName(STYLENAME_ERROR);
    }
  }

  public RValidator getValidator() {
    return validator;
  }

  public void setValidator(RValidator validator) {
    this.validator = validator;
  }

  public void clearValidResults() {
    getTip().clear();
    refreshStyle();
  }

  public boolean isValid() {
    return getTip().isValid();
  }

  public List getInvalidMessages() {
    List list = new ArrayList();
    if (!isValid())
      list.add(getTip().getDisplayed().toMessageRecord(this));
    return list;
  }

  public boolean validate() {
    if (isReadOnly() || !isEnabled() || !isVisible())
      return true;

    getTip().clear();

    if ((fieldDef == null || fieldDef.isNullable()) && isRequired() && getValue() == null) {
      getTip().addError(getTipPrefix() + "不可以为空。");
    }

    if (validator != null) {
      ValidResult validResult = validator.validate(this, getText());
      getTip().add(validResult);
    }

    refreshStyle();
    return isValid();
  }

  public FieldDef getFieldDef() {
    return fieldDef;
  }

  public void setFieldDef(FieldDef fieldDef) {
    this.fieldDef = fieldDef == null ? null : fieldDef.clone();
    if (this.fieldDef == null)
      return;

    setFieldCaption(this.fieldDef.getCaption());
    setRequired(this.fieldDef.isRequired());
    setValidator(new RValidatorOnFieldDef(this.fieldDef));

    if (fieldDef instanceof EnumFieldDef) {
      String[][] values = ((EnumFieldDef) fieldDef).getValues();
      removeAllRadio();
      for (int i = 0; i < values.length; i++) {
        addRadio(values[i][1], values[i][0]);
      }
    } else if (fieldDef instanceof BooleanFieldDef) {
      removeAllRadio();
      addRadio(BooleanFieldDef.VALUE_TEXT_TRUE, Boolean.TRUE);
      addRadio(BooleanFieldDef.VALUE_TEXT_FALSE, Boolean.FALSE);
    }
  }

  public void onBrowserEvent(Event event) {
    switch (DOM.eventGetType(event)) {
    case Event.ONCHANGE:
      event.preventDefault();
      return;
    }

    super.onBrowserEvent(event);
  }

  /**
   * 提示信息的前缀
   * 
   * @return
   */
  private String getTipPrefix() {
    return getFieldCaption() == null ? "" : getFieldCaption() + "：";
  }

  /**
   * 触发change事件，出发前会先进行合法性验证
   * 
   */
  private void fireChange() {
    validate();
    if (!isValid())
      return;

    DomEvent.fireNativeEvent(Document.get().createChangeEvent(), this);
  }

  /**
   * 单选按钮事件处理
   * 
   * @author LiQi
   * 
   */
  private class RadioButtonHandler implements ClickHandler {
    public void onClick(ClickEvent event) {
      RadioItem item = getItemByButton((RadioButton) event.getSource());
      if (!item.equals(selected)) {
        selected = item;
        fireChange();
      }

    }
  }

  /**
   * 一个单选按钮对应的内存数据
   * 
   * @author LiQi
   * 
   */
  private class RadioItem {

    /** 显示的文本 */
    private String text;

    /** 对应的值 */
    private Object value;

    /** 对应的按钮 */
    private RadioButton button;

    public RadioItem(String text, Object value) {
      setText(text);
      setValue(value);
    }

    public String getText() {
      return text;
    }

    public void setText(String text) {
      this.text = text;
    }

    public Object getValue() {
      return value;
    }

    public void setValue(Object value) {
      this.value = value;
    }

    public RadioButton getButton() {
      return button;
    }

    public void setButton(RadioButton button) {
      this.button = button;
    }
  }
}
